/**
 *	@brief  File contains the core of the pluglib library and some convenience functions
 *	@file	Engine.hpp contains Engine class with collection of factory servers and 
 *			collection of all the loaded plugins. Singleton class. 
 *	@author Szymon Janikowski
 *	@author Pawel Gilewski
 */
#ifndef ENGINE_H
#define ENGINE_H


#include "FactoriesServer.hpp"
#include "Plugin.hpp"

#include <map>
#include <vector>
#include <string>
#include <boost/thread/mutex.hpp>
#include <boost/shared_ptr.hpp>
/// @brief Namespace of the plugin support library
namespace pluglib
{
	using namespace std;

	/**
	 * @brief	Singleton class containing FactoriesServer objects and Plugin objects
	 * @note	Entry point for querying for objects with implementations located externally
	 */
	class Engine
	{
	public:
		/// @brief Singleton class method
		/// @note Meyers Singleton Pattern is used here
		static Engine& getInstance()
		{
			static Engine instance;
			return instance;
		}

		///	@brief Adds FactoriesServer object to servers collection.
		///	@param name desired name of the added server
		///	@param server which would be added
		void addServer(string name, FactoriesServerBase* server)
		{
			boost::mutex::scoped_lock(mutex);
			//checks if server is already in the collection
			if (servers_.find(name) == servers_.end())
			{
				servers_[name] = boost::shared_ptr<FactoriesServerBase>(server);
				#ifdef DEBUG_F
				cout << "Server "<< name << " added to collection" << endl;
				#endif
			}
			else
			{
				#ifdef DEBUG_F
				cout << "Server's name already in servers collection ";
				#endif
			}
		}

		///	@brief Retrievs a server of a given name.
		/// @note Needs casting to appropriate FactoriesServer<T> afterwards
		/// @param name of the desired  server
		FactoriesServerBase* getServer(string name)
		{
			boost::mutex::scoped_lock(mutex);
			ServersMapIterator serversMapIterator;
			serversMapIterator = servers_.find(name);
			 if (serversMapIterator != servers_.end()) 
			 {
				#ifdef DEBUG_F
				cout << "Server "<< name << " found" << endl;
				#endif
				 return serversMapIterator->second.get();
			 }
			else 
			{
				#ifdef DEBUG_F
				cout << "Server "<< name << " not found in collection" << endl;
				#endif
				return NULL;
			}
		}

		/// @brief	Main function for loading plugins
		/// @param	fileName path to the plugin
		/// @note	Only this function should be called by user for loading plugins. The others, members of Plugin class
		///			are support functions and are called automaticaly.
		bool loadPlugin(string fileName)
		{
			boost::mutex::scoped_lock(mutex);
			bool success = false;
			if(plugins_.find(fileName) ==  plugins_.end())
			{
				//calling constructor retrieving external function pointer
				Plugin* newPlugin = new Plugin(fileName,success);
				#ifdef DEBUG_F
					if(success)
						cout << "Plugin "<< fileName << " loaded successfully" << endl;
					else
						cout << "Plugin "<< fileName << " not loaded" << endl;
				#endif
				if(success)
				{
					plugins_[fileName] = boost::shared_ptr<Plugin>(newPlugin);
					try{
					      #ifdef DEBUG_F
					      if(success)
						cout << " Trying to register plugin" << endl;	     
					      #endif
						//registering plugin - calling the external function
						newPlugin->registerPlugin(Engine::getInstance());
					}
					catch(...)
					{
						#ifdef DEBUG_F
							cout << "Exception during execution of registerPlugin function of plugin "<< fileName << endl;
						#endif
					}
				}
			}
			return success;
		}

		/// @brief Fills the given vector with name of interfaces kept in servers_
		/// @note Empty vector should be given - the function is appending names
		void getInterfaceNames(vector<string> &names)
		{
			boost::mutex::scoped_lock(mutex);
			if (!servers_.empty())
			{
				ServersMapIterator smit;
				names.clear();
				for(smit = servers_.begin(); smit != servers_.end(); ++smit);
					names.push_back(smit->first);
				#ifdef DEBUG_F
				cout << names.size() << " names obtained" << endl;
				#endif
			}
			#ifdef DEBUG_F
			else
				cout << "Servers collection empty!" << endl;
			#endif
		}
	private:
		Engine(){}
		
		Engine(const Engine&){}

		~Engine()
		{
			/*for (PluginsMapIterator it = plugins_.begin();it !=plugins_.end();++it)
				delete it->second;
			for	(ServersMapIterator it = servers_.begin();it != servers_.end();++it)
				delete it->second;
				*/
		}


		typedef map<string,boost::shared_ptr<FactoriesServerBase> >	ServersMap;
		typedef ServersMap::iterator								ServersMapIterator;

		typedef map<string,boost::shared_ptr<Plugin> >						PluginsMap;
		typedef	PluginsMap::iterator								PluginsMapIterator;

		ServersMap servers_;
		PluginsMap plugins_;

		boost::mutex mutex;
	};//class Engine
};//namespace pluglib
#endif